from dataclasses import dataclass
from typing import List, ClassVar
from collections import Counter
from itertools import combinations_with_replacement
from tqdm import tqdm


def effect2str(eff):
    T = {0: '不变', -7: '缩短7分', 8: '提升8分'}
    LV = '0 I II III IV V VI VII VIII IX X'.split()
    effects = list(eff['effects'].items())
    effects = sorted(effects, key=lambda x: x[1], reverse=True)
    eff_str = " ".join(f"{e}{LV[t]}" for e, t in effects)
    return f'药水时间{T[eff["time"]]} {eff_str}'


@dataclass
class Herb:
    IGNORE_RANDOM: ClassVar[bool] = False
    all_herbs: ClassVar[dict] = {}
    name: str
    color: str
    all_effects: List[str]

    def __post_init__(self):
        Herb.all_herbs[self.name] = self

    @property
    def special(self):
        return self.all_effects[-1]

    @property
    def effects(self):
        eff = self.all_effects[:-1]
        if self.IGNORE_RANDOM:
            try:
                eff.remove('随机效果')
            except ValueError:
                pass
        return eff

    @property
    def has_random_effect(self):
        return '随机效果' in self.all_effects

    @staticmethod
    def load():
        # Data from wiki, add herbs (web spider deleted)
        return [
            Herb('烈焰花', '黄色', ['急迫', '防火', '力量', '缓慢', '药水时间缩短7分']),
            Herb('水灵草', '黄色', ['速度', '水下呼吸', '冰缓', '魔导', '药水时间缩短7分']),
            Herb('夜光草', '黄色', ['跳跃提升', '夜视', '感性', '诅咒', '药水等级下降1级']),
            Herb('寒冰玫瑰', '蓝色', ['生命恢复', '冰缓', '看穿', '出血', '药水等级提升1级']),
            Herb('萤光草', '黄色', ['抗性提升', '急迫', '虚弱', '魔药', '药水时间提升8分']),
            Herb('深红香茅', '红色', ['防火', '生命恢复', '出血', '力量', '药水等级提升1级']),
            Herb('龙舌兰草', '绿色', ['水下呼吸', '箭术', '看穿', '诅咒', '药水等级下降1级']),
            Herb('火焰荆棘', '黑色', ['夜视', '防火', '魔药', '抗性提升', '药水等级下降1级']),
            Herb('死亡花', '黑色', ['贪婪', '生命恢复', '箭术', '凋零', '药水时间提升8分']),
            Herb('祝福之心', '红色', ['迅捷', '生命恢复', '铁皮', '抗性提升', '药水时间缩短7分']),
            Herb('深红草', '红色', ['精确', '贪婪', '迅捷', '出血', '药水等级提升1级']),
            Herb('圣光花', '黄色', ['箭术', '贪婪', '防火', '迅捷', '药水等级下降1级']),
            Herb('影子草', '黑色', ['魔导', '凋零', '幸运', '抗性提升', '药水时间提升8分']),
            Herb('冰雪花', '蓝色', ['看穿', '冰缓', '精确', '魔药', '药水时间提升8分']),
            Herb('痛苦草', '红色', ['感性', '出血', '精确', '魔药', '药水等级提升1级']),
            Herb('诅咒花', '红色', ['感性', '出血', '精确', '魔药', '药水等级下降1级']),
            Herb('黄金蕨', '黄色', ['铁皮', '贪婪', '幸运', '失手', '药水等级下降1级']),
            Herb('蓝晶花', '蓝色', ['幸运', '急迫', '夜视', '失手', '药水时间提升8分']),
            Herb('紫玫瑰', '蓝色', ['看穿', '中毒', '力量', '箭术', '药水等级下降1级']),
            Herb('火猝花', '黄色', ['速度', '防火', '跳跃提升', '迅捷', '药水时间提升8分']),
            Herb('魔凝雏', '白色', ['跳跃提升', '魔导', '出血', '跳跃提升', '药水时间提升8分']),
            Herb('凋零玫瑰', '黑色', ['生命恢复', '凋零', '箭术', '力量', '药水时间提升8分']),
            Herb('诡异菌', '蓝色', ['抗性提升', '夜视', '水下呼吸', '跳跃提升', '药水等级下降1级']),
            Herb('黄金玫瑰', '黄色', ['铁皮', '贪婪', '防火', '缓慢', '药水时间缩短7分']),
            Herb('葡萄藤', '绿色', ['毅力', '饥饿', '急迫', '速度', '药水时间提升8分']),
            Herb('水月花', '蓝色', ['水下呼吸', '水下呼吸', '夜视', '冰缓', '药水时间提升8分']),
            Herb('紫月蕨', '蓝色', ['贪婪', '中毒', '出血', '铁皮', '药水等级提升1级']),
            Herb('噩梦草', '黑色', ['迅捷', '失手', '魔药', '魔导', '药水等级下降1级']),
            Herb('矢车菊', '蓝色', ['精确', '魔导', '急迫', '跳跃提升', '药水时间提升8分']),
            Herb('白月花', '白色', ['箭术', '速度', '铁皮', '幸运', '药水等级下降1级']),
            Herb('管藤', '绿色', ['魔导', '感性', '失手', '出血', '药水时间提升8分']),
            Herb('咒术疣', '黑色', ['魔药', '毅力', '铁皮', '凋零', '药水等级提升1级']),
            Herb('羊肚菌', '红色', ['防火', '魔导', '甜蜜', '脱水', '感性', '药水时间缩短7分']),
            Herb('松茸', '红色', ['水下呼吸', '力量', '甜点', '虚弱', '毅力', '药水时间提升8分']),
            Herb('松乳菇', '黄色', ['夜视', '箭术', '甜点', '冰缓', '铁皮', '药水时间提升8分']),
            Herb('牛肝菌', '白色', ['贪婪', '急迫', '甜蜜', '出血', '幸运', '药水等级提升1级']),
            Herb('毒蝇伞', '红色', ['魔导', '力量', '箭术', '剧毒', '看穿', '药水等级下降1级']),
            Herb('墨汁鬼伞', '蓝色', ['速度', '防火', '迅捷', '急迫', '毅力', '药水时间缩短7分']),
            Herb('环柄菇', '白色', ['力量', '甜蜜', '抗性提升', '跳跃提升', '铁皮', '药水时间提升8分']),
            Herb('蜜环菌', '黄色', ['生命恢复', '迅捷', '跳跃提升', '诅咒', '幸运', '药水时间缩短7分']),
            Herb('鸡油菌', '黄色', ['精确', '迅捷', '魔药', '失手', '看穿', '药水等级下降1级']),
            Herb('紫丁香蘑', '蓝色', ['急迫', '随机效果', '箭术', '中毒', '看穿', '药水等级提升1级']),
            Herb('秀珍菇', '黑色', ['生命恢复', '随机效果', '魔导', '凋零', '幸运', '药水时间缩短7分']),
            Herb('绿褶菇', '黄色', ['魔药', '精确', '甜蜜', '速度', '铁皮', '药水等级下降1级']),
            Herb('橘黄裸伞', '黄色', ['魔导', '精确', '水下呼吸', '缓慢', '毅力', '药水时间提升8分']),
            Herb('花菇', '红色', ['箭术', '生命恢复', '力量', '饥饿', '感性', '药水等级提升1级']),
            Herb('粉褶菌', '蓝色', ['随机效果', '生命恢复', '甜蜜', '虚弱', '毅力', '药水等级下降1级']),
            Herb('毒鹅膏', '白色', ['抗性提升', '防火', '箭术', '剧毒', '铁皮', '药水时间提升8分'])
        ]

    @staticmethod
    def calculate_effects(herbs, verbose=False):
        if len(Herb.all_herbs) == 0:
            Herb.load()
        if verbose:
            print('目标药草：', herbs)
        if isinstance(herbs, str):
            herbs = herbs.strip().split(' ')
        for h in herbs:
            if h not in Herb.all_herbs:
                raise KeyError(f'Herb "{h}" not found')
        # Special effects
        sp = Counter(sum(([Herb.all_herbs[h].special] for h in herbs), []))
        for k in list(sp.keys()):
            sp[k] = sp[k] // 2
            if sp[k] == 0:
                del sp[k]
            if k.startswith('药水时间'):  # No more than 1 times
                sp[k] = min(sp[k], 1)
            if k.startswith('药水等级'):  # No more than 2 times
                sp[k] = min(sp[k], 2)
        # Normal effects
        eff = Counter(sum((Herb.all_herbs[h].effects for h in herbs), []))
        potion_time = 0
        for k in list(eff.keys()):
            if eff[k] < 2:
                del eff[k]
        for k in sp:
            if k.startswith('药水时间'):
                potion_time += sum((8 if k == '药水时间提升8分' else -7) for _ in range(sp[k]))
            else:
                for _ in range(sp[k]):
                    for e in eff:
                        if k == '药水等级提升1级':
                            eff[e] += 2
                        else:
                            eff[e] = max(2, eff[e] - 2)

        for k in list(eff.keys()):
            eff[k] = eff[k] // 2
            if eff[k] == 0:
                del eff[k]

        if verbose:
            print('药水效果：', eff)
            print(f'药水时间变化：{potion_time:+d}')
        return dict(time=potion_time, effects=eff)

    @staticmethod
    def find_recipe(target_effects: str, top=10, avoid_effects=None):
        if len(Herb.all_herbs) == 0:
            Herb.load()
        if avoid_effects is None:
            avoid_effects = []
        if isinstance(avoid_effects, str):
            avoid_effects = avoid_effects.strip().split()
        if isinstance(target_effects, str):
            target_effects = target_effects.strip().split()
        N_HERBS = 5
        herbs = list(filter(lambda h: '药水等级提升1级' in h.all_effects or sum(
            e in target_effects for e in h.all_effects) > 0, Herb.all_herbs.values()))
        recipe_list = []
        for herb_recipe in tqdm(combinations_with_replacement(herbs, N_HERBS)):
            herb_recipe = list(map(lambda h: h.name, herb_recipe))
            potion_effects = Herb.calculate_effects(herb_recipe)
            if sum((e in avoid_effects) for e in potion_effects['effects']) == 0:
                recipe_list.append((herb_recipe, potion_effects))
        recipe_list = sorted(recipe_list, key=lambda x: sum(
            x[1]['effects'][e] * 100 for e in target_effects) + len(x[1]['effects']), reverse=True)
        print('Valid herbs:', ' '.join(list(map(lambda h: h.name, herbs))))
        print(f'Top {top} results:')

        for herb_recipe, potion_effects in recipe_list[:top]:
            recipe_str = '+'.join(herb_recipe)
            print(f'{recipe_str}{" "*(50-len(recipe_str)*2)}{effect2str(potion_effects)}')

    def __repr__(self):
        return f'Herb(name={self.name!r}, color={self.color!r}, effects={self.effects!r}, ' \
               f'special={self.special!r}, has_random_effect={self.has_random_effect!r})'

    def __str__(self):
        return f'{self.name}: {" ".join(self.all_effects)}'


EFFECTS_LIST = '缓慢|魔导|防火|冰缓|速度|精确|生命恢复|感性|魔药|出血|贪婪|看穿|夜视|铁皮|虚弱|' \
               '力量|毅力|凋零|迅捷|幸运|失手|急迫|水下呼吸|抗性提升|饥饿|箭术|跳跃提升|诅咒|中毒|' \
               '甜蜜|脱水|甜点|剧毒|随机效果|药水时间缩短7分|药水时间提升8分|药水等级下降1级|药水等级提升1级'.split('|')

if __name__ == '__main__':
    Herb.find_recipe(target_effects='魔药 魔导',
                     avoid_effects='凋零 随机效果 缓慢 失手 脱水 出血 虚弱 饥饿 看穿',
                     top=999)
